package cn.openea.perpartner.meily.aspect;

import cn.hutool.core.util.StrUtil;
import cn.openea.perpartner.common.core.exception.LockTimeoutException;
import cn.openea.perpartner.common.core.utils.ServletUtils;
import cn.openea.perpartner.common.core.web.domain.AjaxResult;
import cn.openea.perpartner.common.redis.service.RedissonService;
import cn.openea.perpartner.meily.aspect.annotation.AccessLimit;
import cn.openea.perpartner.meily.util.UserContext;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.servlet.http.HttpServletRequest;

/**
 * User:zane_ysk
 */
@Aspect
@Component
@Slf4j
@RequiredArgsConstructor
public class AccessLimitAspect {

    private static final String FORM_SUBMIT_EVENT_KEY = "SUBMIT_EVENT_KEY_";

    private final RedissonService redissonService;

    @Pointcut("@annotation(accessLimit)")
    public void pointCut(AccessLimit accessLimit) {

    }

    @Around(value = "pointCut(accessLimit)", argNames = "pjp,accessLimit")
    public Object around(ProceedingJoinPoint pjp, AccessLimit accessLimit) throws Throwable {
        log.info("intoRepeatSubmitAspect");

        int lockSeconds = accessLimit.lockTime();

        HttpServletRequest request = ServletUtils.getRequest();
        Assert.notNull(request, "request can not null");

        String token = UserContext.getUserId();
        String path = request.getServletPath();

        if (StrUtil.isBlank(token)) {
            // 如果userId不存在,用sessionId
            token = request.getSession().getId();
        }

        if (StrUtil.isBlank(ServletUtils.getReferer(request))) {//如果没有referer，很可能是服务器请求的，使用ip不是用sessionid
            token = ServletUtils.getRemoteIP(request);
        }

        String key = getKey(token, path);

        boolean isSuccess = redissonService.lockSeconds(key, 0, lockSeconds);

        if (isSuccess) {
            log.info("tryLock success, key = [{}], lockSeconds = [{}]", key, lockSeconds);
            // 获取锁成功, 执行进程
            Object result;
            try {
                result = pjp.proceed();
            } catch (LockTimeoutException runtimeException) {
                if (accessLimit.unlockWhenThrowException()) {
                    redissonService.unlock(key);
                }
                throw runtimeException;
            } finally {
                // 解锁
                log.info("releaseLock success, key = [{}]", key);
            }
            return result;
        } else {
            // 获取锁失败，认为是重复提交的请求
            log.info("tryLock fail, key = [{}]", key);
            return AjaxResult.error(600, "请求频率过快，请" + lockSeconds + "秒后再进行尝试");
        }
    }


    private String getKey(String token, String path) {
        return FORM_SUBMIT_EVENT_KEY + token + path;
    }


}
